import org.jgrapht.*;
import org.jgrapht.graph.*;

import org.jgrapht.alg.*; // TEMP TODO consider rmeove.

import java.io.FileWriter;
import java.io.IOException;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.HashMap;
import java.util.Vector;

/**************************************************
 * Main.java
 * @author liatperl
 * Last updater: liat
 * Date: 6.8.09 19:00
 * 
 * Refactoring ideas:
 * - Add gene index (number instead of string)
 * - Annotations - consider changing to int (based on GO term finder).
 * - Use RPC / XML server to activate perl from Java.
 * - Contain geneMap and the graph inside a Network class.
 */

public class Main {
	
	
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		final int MIN_CLUSTER_SIZE = 3;

//		double[] alphas = {0, 0.25, 0.5, 0.75, 0.85, 0.9, 0.99};
//		for (double alpha: alphas) {
//			experiments(alpha);	
//		}
		
		experiments2(false, 0.75, "lomeshane");
		// experiments2(false, 0.75, "allnets_nb_a075.csv");
		// experiments2(false, 0, "allnets_nb_a000.csv");
		// experiments2(true, 0.9, "allnets_mcl_a090.csv");		
		// experiments2(true, 0, "allnets_mcl_a000.csv");		
		// experiments(0);
		
		
		// TEMP TODO REMOVE if you want to test...
		if (true)
			return;
		

		String netFileName = args[0];
		
		// Load network data structures.
		HashMap<String, Gene> geneMap = new HashMap<String, Gene>();
		SimpleWeightedGraph<String, DefaultWeightedEdge> net = 
			PPIFactory.generate(geneMap, netFileName);
	
// 		System.out.println(net.toString()); // TEMP TODO REMOVE
		
		System.out.println("ANNOTATIONS");
		System.out.println("******************");		

		AnnotationsManager a = new AnnotationsManager();
		int annotatedGenesInGenome = a.load("./data/gene_ontology_edit.obo", "./data/gene_assoc_human.txt", geneMap);

		// Annotations tests.
		System.out.println("Annotated genes in genome: " + annotatedGenesInGenome);
		
		Gene calm2Gene = geneMap.get("PTPN1");
		if (calm2Gene.hasAnnotation("GO:0004725", a)) {
			System.out.println("PTPN1 explicit annotation.\tGOOD");
		}
		else {
			System.out.println("PTPN1 explicit annotation.\tBAD");			
		}
		if (calm2Gene.hasAnnotation("GO:0003824", a)) {
			System.out.println("PTPN1 implicit annotation.\tGOOD");
		}
		else {
			System.out.println("PTPN1 implicit annotation.\tBAD");			
		}
		if (!calm2Gene.hasAnnotation("GO:0009710", a)) {
			System.out.println("PTPN1 mismatch annotation.\tGOOD");
		}
		else {
			System.out.println("PTPN1 mismatch annotation.\tBAD");			
		}
		
		System.out.println("GO:0005487 count: " + a.count("GO:0005487")+ "\tshould be: 6\t" + 
				((a.count("GO:0005487") == 6)? "GOOD": "BAD"));
		System.out.println("GO:0005215 count: " + a.count("GO:0005215")+ "\tshould be: 616\t" + 
				((a.count("GO:0005215") == 616)? "GOOD": "BAD"));
		System.out.println("GO:0008150 count: " + a.count("GO:0008150")+ "\tshould be: 8900\t" + 
				((a.count("GO:0008150") == 8900)? "GOOD": "BAD"));		
		
		// Create manipulators
		ClusterListManipulator enrichLegalizer = new CLMEnrichmentLegalizer(a, net);
		ClusterListManipulator neighborsAdder = new CLMAddUnclusteredNeighbours(geneMap, a, net); 		
		
		// MCL
		System.out.println("MCL");
		System.out.println("*****");
		
		ClusteringAlgorithms mcl = new MarkovClustring();
		LinkedList<Cluster> clusters =  mcl.getClusters(net, geneMap);
/*		for (int i=0; i < clusters.size(); i++) {
			System.out.println(clusters.get(i));
		}*/

		System.out.println("Network data:");
		System.out.println("\tNodes: " + net.vertexSet().size());
		System.out.println("\tEdges: " + net.edgeSet().size());
		System.out.println("\tDensity: " + (double)(net.edgeSet().size()) * 2.0 / (net.vertexSet().size() * (net.vertexSet().size() - 1)));
		System.out.println("\tClusters: "+ clusters.size());
		
		countOverlapsAndUnclustered(geneMap);

		Iterator<Cluster> clusterIter = clusters.iterator();
		while (clusterIter.hasNext()) {
			Cluster c = clusterIter.next();
//			if ((c.size() < MIN_CLUSTER_SIZE) || (!c.hasAnnotatedGenes())) {
			if (c.size() < MIN_CLUSTER_SIZE) {
				c.clear();
				clusterIter.remove();
			}
		}
		
		System.out.println("After removal of 2-sized clusters");
		System.out.println("\tClusters: "+ clusters.size());
		
		countOverlapsAndUnclustered(geneMap);
		
	/*	
		// Test removing gene from cluster
		Iterator<Cluster> citer = clusters.iterator();
		citer.next();citer.next();citer.next();citer.next();
		Cluster ctemp = citer.next();
		System.out.println("Before: " + ctemp.toString());
		System.out.println("Clusters ATXN1 participate in: " + geneMap.get("ATXN1").clustersCount());		
		ctemp.remove(geneMap.get("ATXN1"));
		System.out.println("After: " + ctemp.toString());
		System.out.println("Clusters ATXN1 participate in: " + geneMap.get("ATXN1").clustersCount());
*/
		
		
		System.out.println("GO TERM FINDER");
		System.out.println("**********************");
		double cutoff = 0.051;
		TermFinder gtf = new TermFinder(cutoff, annotatedGenesInGenome, 
				"termFinder.pl", "gene_ontology_edit.obo", "gene_assoc_human.txt" );
		gtf.find(clusters);
		
		System.out.println("AFTER GO TERM FINDER AND FILTERING NON ANNOTATED CLUSTERS:");
		
		Iterator<Cluster> citer = clusters.iterator();
		while (citer.hasNext()) {
			Cluster c = citer.next();
			if (c.isAnnotated()) {
				System.out.println(c.getAnnotation() + "\t" +  c.toString());	
			}
			else {
				c.clear();
				citer.remove();
			}
		}
		
		ClusteringEvaluator evaluator 				= new ClusteringEvaluator(a, geneMap);
		ClusteringEvaluator.Results evalStats 	= new ClusteringEvaluator.Results();
		double clusteringGrade = evaluator.evaluate(clusters, evalStats);
		
		System.out.println("\tCLUSTERING EVALUATION, MCL");
		System.out.println("\t\t* No manipulators:");
		System.out.println("\t\tClusters: "+ clusters.size());
		countOverlapsAndUnclustered(geneMap);		
		System.out.println("\t\t\tFraction: " + clusteringGrade);
		System.out.println("\t\t\tAnnotated genes in correct clusters:\t" + evalStats.totalGenesInClustersWDominantAnnot);
		System.out.println("\t\t\tAnnotated genes total:\t" + evalStats.totalAnnotatedGenes);		

		enrichLegalizer.manipulate(clusters);
		
		clusteringGrade = evaluator.evaluate(clusters, evalStats);		
		System.out.println("\t\t* remove non-essential nodes:");
		System.out.println("\t\tClusters: "+ clusters.size());
		countOverlapsAndUnclustered(geneMap);
		System.out.println("\t\t\tFraction: " + clusteringGrade);
		System.out.println("\t\t\tAnnotated genes in correct clusters:\t" + evalStats.totalGenesInClustersWDominantAnnot);
		System.out.println("\t\t\tAnnotated genes total:\t" + evalStats.totalAnnotatedGenes);		

		neighborsAdder.manipulate(clusters);
		
		citer = clusters.iterator();
		while (citer.hasNext()) {
			Cluster c = citer.next();
			if (c.size() < 3) {
				c.clear();
				citer.remove();
			}			
		}		
		
		clusteringGrade = evaluator.evaluate(clusters, evalStats);		
		System.out.println("\t\t* add unclustered, correctly annotated neighbors (cluster size < 3):");
		System.out.println("\t\tClusters: "+ clusters.size());
		countOverlapsAndUnclustered(geneMap);
		System.out.println("\t\t\tFraction: " + clusteringGrade);
		System.out.println("\t\t\tAnnotated genes in correct clusters:\t" + evalStats.totalGenesInClustersWDominantAnnot);
		System.out.println("\t\t\tAnnotated genes total:\t" + evalStats.totalAnnotatedGenes);		
		
		// Clear MCL stuff
		for (Cluster clus: clusters) {
			clus.clear();
		}

		System.out.println("\nAfter clear:");		
		countOverlapsAndUnclustered(geneMap);
		
		System.out.println("\nNETWORK BLAST");
		System.out.println("*************************");		
		NetworkBlast blastNet = new NetworkBlast();
		clusters = blastNet.getClusters(net, geneMap);

		System.out.println("\tClusters: "+ clusters.size());
		countOverlapsAndUnclustered(geneMap);
		
		// Check how nasty is the overlap...
		System.out.println("How genes have k overlaps (0..64)");
		int overlaps[] = new int[65];
		for (Gene g: geneMap.values()) {
			overlaps[g.clustersCount()]++;
		}
		for (int i = 0; i < overlaps.length; i++) {
			System.out.print(overlaps[i] + "\t"); 
		}
		System.out.println();
		System.out.println("How overlaping genes each cluster has");
		overlaps = new int[21];
		int moreThanHalf = 0;
		int moreThanQuater = 0;
		for (Cluster c: clusters) {
			int overlappingGenes = 0;
			for (Gene g: c) {
				if (g.clustersCount() > 1) {
					overlappingGenes++;
				}
			}
			
			overlaps[overlappingGenes]++;
			
			if (overlappingGenes > (c.size() / 4)) {
				moreThanQuater++;
				if (overlappingGenes > (c.size() / 2)) {
					moreThanHalf++;
				}
			}
		}
		for (int i = 0; i < overlaps.length; i++) {
			System.out.print(overlaps[i] + "\t"); 
		}
		System.out.println("Clusters with > 25% overlapping genes: " + moreThanQuater);
		System.out.println("Clusters with > 50% overlapping genes: " + moreThanHalf);		
		System.out.println();
		
//		OverlapsSolver olSolver = new OverlapsSolverRemoveWeakerCluster();
		OverlapsSolver olSover = new OverlapsSolverRWCAllowSingleOL(3, net);
		olSover.solve(clusters);
		
		System.out.println("\tOverlap resolve: After using weaker cluster remover");
		System.out.println("\tClusters: "+ clusters.size());
		countOverlapsAndUnclustered(geneMap);
		
		System.out.println("GO TERM FINDER FOR NETWORK BLAST");
		gtf.find(clusters);

		citer = clusters.iterator();
		while (citer.hasNext()) {
			Cluster c = citer.next();
			if (c.isAnnotated()) {
				System.out.println(c.getAnnotation() + "\t" + c.toString());	
			}
			else {
				c.clear();
				citer.remove();
			}
		}
		

		clusteringGrade = evaluator.evaluate(clusters, evalStats);
		
		System.out.println("\tCLUSTERING EVALUATION, NetworkBLAST");
		System.out.println("\t\t* No manipulators:");
		System.out.println("\t\tClusters: "+ clusters.size());
		countOverlapsAndUnclustered(geneMap);		
		System.out.println("\t\t\tFraction: " + clusteringGrade);
		System.out.println("\t\t\tAnnotated genes in correct clusters:\t" + evalStats.totalGenesInClustersWDominantAnnot);
		System.out.println("\t\t\tAnnotated genes total:\t" + evalStats.totalAnnotatedGenes);		

		enrichLegalizer.manipulate(clusters);
		
		clusteringGrade = evaluator.evaluate(clusters, evalStats);		
		System.out.println("\t\t* remove non-essential nodes:");
		System.out.println("\t\tClusters: "+ clusters.size());
		countOverlapsAndUnclustered(geneMap);
		System.out.println("\t\t\tFraction: " + clusteringGrade);
		System.out.println("\t\t\tAnnotated genes in correct clusters:\t" + evalStats.totalGenesInClustersWDominantAnnot);
		System.out.println("\t\t\tAnnotated genes total:\t" + evalStats.totalAnnotatedGenes);

		neighborsAdder.manipulate(clusters);
		
		clusteringGrade = evaluator.evaluate(clusters, evalStats);		
		
		citer = clusters.iterator();
		while (citer.hasNext()) {
			Cluster c = citer.next();
			if (c.size() < 3) {
				c.clear();
				citer.remove();
			}			
		}
		
		System.out.println("\t\t* add unclustered, correctly annotated neighbors (clusters size > 3:");
		System.out.println("\t\tClusters: "+ clusters.size());
		countOverlapsAndUnclustered(geneMap);
		System.out.println("\t\t\tFraction: " + clusteringGrade);
		System.out.println("\t\t\tAnnotated genes in correct clusters:\t" + evalStats.totalGenesInClustersWDominantAnnot);
		System.out.println("\t\t\tAnnotated genes total:\t" + evalStats.totalAnnotatedGenes);		
		
		//		for (DefaultWeightedEdge e: net.edgeSet()) {
		
//
//		}
		
		/*
		String netFileName = args[0];
		// initialize the mapping hash
		MapToAnnot.init(); // TEMP TODO change from static.

		BuildNet build = new BuildNet();
		Network net = build.generateNet(netFileName);
		//net.printNet();
		//System.out.println("\n\n");
		ClusteringAlgorithms mcl = new MarkovClustring();
		LinkedList<Cluster> clusters =  mcl.getClusters(net);
		for (int i=0; i < clusters.size(); i++) {
			System.out.println(clusters.get(i));
		}
		*/
		
		/** Test Clusters with 1.sf **/
		// String netFileName = args[0];
		// initialize the mapping hash

		
		/*
		// Old, non-jgrapht implementation
		MapToAnnot.init(); // TEMP TODO change from static.

		BuildNet build = new BuildNet();
		Network net = build.generateNet(netFileName);
		//net.printNet();
		//System.out.println("\n\n");
		ClusteringAlgorithms mcl = new MarkovClustring();
		LinkedList<Cluster> clusters =  mcl.getClusters(net);
		for (int i=0; i < clusters.size(); i++) {
			System.out.println(clusters.get(i));
		}

		System.out.println("Network data:");
		System.out.println("\tNodes: " + net.nodesCount());
		System.out.println("\tEdges: " + net.edgesCount());
		System.out.println("\tDensity: " + (double)(net.edgesCount()) * 2.0 / (net.nodesCount() * (net.nodesCount() - 1)));
		System.out.println("\tClusters: "+ clusters.size());
		
		Iterator<Node> nodesIter = net.nodesIter();
		int countUnclustered = 0;
		int countUnclusteredLonely = 0;		
		int countMultiClustered = 0;
		while (nodesIter.hasNext()) {
			Node currNode = nodesIter.next();
			if (currNode.clustersCount() > 1) {
				countMultiClustered++;
			}
			else if (currNode.clustersCount() == 0) {
				countUnclustered++;
				if (currNode.getDegree() == 0) {
					countUnclusteredLonely++;
				}
			}
		}
		
		System.out.println("\t\tNodes in multiple clusters: " + countMultiClustered);
		System.out.println("\t\tNodes not in a cluster: " + countUnclustered);

		Iterator<Cluster> clusterIter = clusters.iterator();
		while (clusterIter.hasNext()) {
			Cluster c = clusterIter.next();
			if ((c.size() < MIN_CLUSTER_SIZE) || (!c.hasAnnotatedGenes())) {
				c.clear();
				clusterIter.remove();
			}
		}
		
		
		System.out.println("After removal of 2-sized clusters and non-annotated clusters");
		System.out.println("\tClusters: "+ clusters.size());
		
		nodesIter = net.nodesIter();
		countUnclustered = 0;
		countMultiClustered = 0;
		while (nodesIter.hasNext()) {
			Node currNode = nodesIter.next();
			if (currNode.clustersCount() > 1) {
				countMultiClustered++;
			}
			else if (currNode.clustersCount() == 0) {
				countUnclustered++;
			}
		}
		
		System.out.println("\t\tNodes in multiple clusters: " + countMultiClustered);
		System.out.println("\t\tNodes not in a cluster: " + countUnclustered);
		
		for (Cluster c: clusters) {
			System.out.println(c);
		}
		
		int anocount = 0;
		Iterator<Node> nit = net.nodesIter();
		while (nit.hasNext()) {
			if (!nit.next().isAnnotated()) {
				++anocount;
			}
		}
		System.out.println("Number of unannotated genes: " + anocount + " out of " + net.nodesCount());
		
		/** TEST CLUSTER**/
		/*
		Node n1 = new Node("Meni");
		Node n2 = new Node("Liat");
		Node n3 = new Node("Adi");
		Node n4 = new Node("Oded");
		Cluster clus = new Cluster();
		clus.add(n1);
		clus.add(n2);
		clus.add(n3);
		System.out.println("Test add and contains");
		System.out.println(clus.contains(n1)== true ? "PASSED" : "FAILED");
		System.out.println(clus.contains(n3)== true ? "PASSED" : "FAILED");
		System.out.println(clus.contains(n4)== false ? "PASSED" : "FAILED");
		System.out.println("TEST ITERATOR");
		Iterator<Node> iter = clus.iterator();
		while (iter.hasNext()) {
			System.out.println(iter.next().getName());
		}
		*/
		
		/** TESTS **/
		// Degree test
		/*
		System.out.print("TEST getDegree():\t");
		System.out.println(net.getNode("ODED").getDegree() == 3 ? "PASSED": "FAILED"); 
		
		// hasNode test
		System.out.print("TEST hasNode (existing node):\t");
		System.out.println(net.hasNode("ODED") ? "PASSED": "FAILED");
		System.out.print("TEST hasNode (bad node):\t");
		System.out.println(!net.hasNode("BLABLA") ? "PASSED": "FAILED");		

		// getNode test
		System.out.print("TEST getNode (existing node):\t");
		System.out.println(net.getNode("ODED").getName().equals("ODED") ? "PASSED": "FAILED");
		System.out.print("TEST getNode (bad node):\t");
		System.out.println(net.getNode("BLABLA") == null ? "PASSED": "FAILED");
		
		// hasEdge test
		System.out.print("TEST hasEdge (existing edge):\t");
		System.out.println(net.hasEdge("ADI", "ODED") ? "PASSED": "FAILED");
		System.out.print("TEST hasEdge (existing edge) #2 :\t");
		System.out.println(net.hasEdge("YONI", "ADI") ? "PASSED": "FAILED");		
		System.out.print("TEST hasEdge (bad edge):\t");
		System.out.println(!net.hasEdge("LIRAZ", "ORI") ? "PASSED": "FAILED");
		System.out.print("TEST hasEdge (bad node):\t");
		System.out.println(!net.hasEdge("LIRAZ", "ZEVEL") ? "PASSED": "FAILED");		
		
		// getConf test
		System.out.print("TEST getConf (existing edge):\t");
		System.out.println(net.getConf("YONI", "ADI") == 0.999605 ? "PASSED": "FAILED");		
		System.out.print("TEST getConf (bad edge):\t");
		System.out.println(net.getConf("LIRAZ", "ORI") == 0.0 ? "PASSED": "FAILED");
		System.out.print("TEST getConf (bad node):\t");
		System.out.println(net.getConf("BLABLA", "ORI") == 0.0 ? "PASSED": "FAILED");
		
		// setEdge test
		System.out.print("TEST setEdge (existing edge):\t");
		net.setEdge("LIRAZ", "ODED", 1.0);
		System.out.println(net.getConf("ODED", "LIRAZ") == 1.0 ? "PASSED": "FAILED");
		System.out.print("TEST setEdge (new edge):\t");
		net.setEdge("LIRAZ", "ADI", 0.015);
		System.out.println(net.getConf("ADI", "LIRAZ") == 0.015 ? "PASSED": "FAILED");
		System.out.print("TEST setEdge (bad node):\t");
		net.setEdge("ODED", "URIEL", 0.75);
		System.out.println((net.getConf("ODED", "URIEL") == 0) && !net.hasNode("URIEL") ? "PASSED": "FAILED");

		System.out.print("TEST removeEdge (existing edge):\t");
		net.removeEdge("ADI", "YONI");
		System.out.println(!net.hasEdge("YONI", "ADI") ? "PASSED": "FAILED");
		System.out.print("TEST removeEdge (nonexisting edge):\t");
		net.removeEdge("LIRAZ", "ORI");
		System.out.println(!net.hasEdge("LIRAZ", "ORI") && (net.getNode("ADI").getDegree() == 3) ? "PASSED": "FAILED");	
		
		System.out.println("");
		net.printNet();
		*/
		
		
		/*
		// Test update edge 
		// change the weight of the edge RPS6KA5 () CREB1 
		// to 0.002.
		System.out.println("update the weight of the edge:");
		System.out.println("RPS6KA5 CREB1 0.97554 to 0.002");
		Edge edge = new Edge("RPS6KA5","CREB1",0.975542);
		net.setEdgeWeight(edge, 0.002);
		
		// Test remove edge.
		// Remove edge  (0.17357) RPS6KA5
		System.out.println("remove the edge: HSPB2 RPS6KA5 0.17357");
		net.removeEdge(new Edge("HSPB2","RPS6KA5",0.17357));
		net.printNet();
		*/
	}
	
	protected static void countOverlapsAndUnclustered(HashMap<String, Gene> geneMap) {
		int countUnclustered = 0;
		int countMultiClustered = 0;
		for (Gene g: geneMap.values()) {
			if (g.clustersCount() > 1) {
				countMultiClustered++;
			}
			else if (g.clustersCount() == 0) {
				countUnclustered++;
			}
		}
		
		System.out.println("\t\tGenes in multiple clusters: " + countMultiClustered);
		System.out.println("\t\tGenes not in a cluster: " + countUnclustered);		
	}
	
	/**
	 * Project related experiments!
	 * Test a given algorithm and weight re-calc value over all of the given networks.
	 */
	protected static void experiments2(boolean doMCL, double alpha, String resultFilename) {
		final int MIN_CLUSTER_SIZE = 3;
		double cutoff = 0.05;
		String output = "";
		
//		for (int i = 1; i < 102; ++i) {
		for (int i = 1; i < 2; ++i) {
			try {
//		int[] is = {1,8, 61};
//		for (int i: is) {
			String netFileName = "" + i + ".sif";
			
			HashMap<String, Gene> geneMap = new HashMap<String, Gene>();
			SimpleWeightedGraph<String, DefaultWeightedEdge> net = 
				PPIFactory.generate(geneMap, netFileName);
			
			AnnotationsManager a = new AnnotationsManager();
			int annotatedGenesInGenome = a.load("./data/gene_ontology_edit.obo", "./data/gene_assoc_human.txt", geneMap);

			 int annotatedGenes = 0;
			 for (Gene g: geneMap.values()) {
				 if (g.isAnnotated()) {
					 ++annotatedGenes;
				 }
			 }

			// Create manipulators
			ClusterListManipulator enrichLegalizer = new CLMEnrichmentLegalizer(a, net);
			ClusterListManipulator neighborsAdder = new CLMAddUnclusteredNeighbours(geneMap, a, net);			 

			// ******** Use only when recalculating the weight. ******
			// alter the edges weights.
			if (alpha > 0) {
				WeightFunction alterWeight = new WeightFunction(geneMap,a);
				alterWeight.alterNetWeight(net,alpha);
			}
			
			LinkedList<Cluster> clusters;
			
			if (doMCL) {
				//  Run MCL
				ClusteringAlgorithms mcl = new MarkovClustring();
				clusters =  mcl.getClusters(net, geneMap);

				// Remove size < 3 clusters.
				Iterator<Cluster> clusterIter = clusters.iterator();
				while (clusterIter.hasNext()) {
					Cluster c = clusterIter.next();
					if (c.size() < MIN_CLUSTER_SIZE) {
						c.clear();
						clusterIter.remove();
					}
				}
			}
			else {
				// Do network BLAST
				ClusteringAlgorithms networkBLAST = new NetworkBlast();				
				clusters = networkBLAST.getClusters(net, geneMap);
				 
				// NB - Solve overlaps
				OverlapsSolver olSover = new OverlapsSolverRWCAllowSingleOL(MIN_CLUSTER_SIZE , net);
				olSover.solve(clusters);
			}
			
			TermFinder gtf = new TermFinder(cutoff, annotatedGenesInGenome, 
					"termFinder.pl", "gene_ontology_edit.obo", "gene_assoc_human.txt" );
			gtf.find(clusters);
				
			// Remove non-annotated clusters
			Iterator<Cluster> citer = clusters.iterator();
			while (citer.hasNext()) {
				Cluster c = citer.next();
				if (!c.isAnnotated()) {
					c.clear();
					citer.remove();
				}
			}
				
			// Legalize
			enrichLegalizer.manipulate(clusters);
			// Add neighboring prots
			neighborsAdder.manipulate(clusters);
				
			// Remove size < 3 clusters.
			citer = clusters.iterator();
			while (citer.hasNext()) {
				Cluster c = citer.next();
				if (c.size() < MIN_CLUSTER_SIZE) {
					c.clear();
					citer.remove();
				}
			}
				
			int genesBelongingToCluster = 0;
			for (Gene g: geneMap.values()) {
				if ((g.isAnnotated()) && (g.clustersCount() != 0)) {
					++genesBelongingToCluster;
				}
			}
				
			// Print output
			output += ((double)genesBelongingToCluster / annotatedGenes) + "\n";
			System.out.println("" + i + "\t" + ((double)genesBelongingToCluster / annotatedGenes) + "\t" + genesBelongingToCluster + "\t" + annotatedGenes + "\t" + clusters.size()) ;
			
			// Print clusters
			for (Cluster clus: clusters) {
				System.out.println(clus.getAnnotation() + "\t" + clus.toString());
			}
			
			// Clear clusters - probably not necessary.
			for (Cluster clus: clusters) {
				clus.clear();
			}
			
			}
			catch (Exception e) {
				continue;
			}
		}
		
		// Save to file.
		try {
			FileWriter outputFile = new FileWriter(Consts.DATA_DIR+ resultFilename);
			outputFile.write(output);
			outputFile.close();
		} catch (IOException ioe) {
			System.out.println(ioe.getMessage());
			System.exit(1);
		}
	}
	
	/**
	 * Project-related experiments!
	 * Test a few sample networks for various cutoffs.
	 */
	protected static void experiments(double alpha) {
		final int MIN_CLUSTER_SIZE = 3;
		String output = "";

		
		// Examine the data...
		String[] netFiles = {"1.sif", "8.sif", "29.sif", "42.sif", "61.sif"};
		double[] cutoffs  = {0.1, 0.05, 0.01, 0.001};
		ClusteringAlgorithms mcl = new MarkovClustring();
		ClusteringAlgorithms networkBLAST = new NetworkBlast();		
		
		for (String netFileName: netFiles) {
			// Load network data structures.
			HashMap<String, Gene> geneMap = new HashMap<String, Gene>();
			SimpleWeightedGraph<String, DefaultWeightedEdge> net = 
				PPIFactory.generate(geneMap, netFileName);
			
			AnnotationsManager a = new AnnotationsManager();
			int annotatedGenesInGenome = a.load("./data/gene_ontology_edit.obo", "./data/gene_assoc_human.txt", geneMap);

			 int annotatedGenes = 0;
			 for (Gene g: geneMap.values()) {
				 if (g.isAnnotated()) {
					 ++annotatedGenes;
				 }
			 }

			// Create manipulators
			ClusterListManipulator enrichLegalizer = new CLMEnrichmentLegalizer(a, net);
			ClusterListManipulator neighborsAdder = new CLMAddUnclusteredNeighbours(geneMap, a, net);			 

			// ******** Use only when recalculating the weight. ******
			// alter the edges weights.
			if (alpha > 0) {
				WeightFunction alterWeight = new WeightFunction(geneMap,a);
				alterWeight.alterNetWeight(net,alpha);
			}
	
			LinkedList<Cluster> clusters = null;
			for (double cutoff: cutoffs) { // bug fix...
			//   Run MCL
			clusters =  mcl.getClusters(net, geneMap);

			// Remove size < 3 clusters.
			Iterator<Cluster> clusterIter = clusters.iterator();
			while (clusterIter.hasNext()) {
				Cluster c = clusterIter.next();
				if (c.size() < MIN_CLUSTER_SIZE) {
					c.clear();
					clusterIter.remove();
				}
			}			
			
//			for (double cutoff: cutoffs) {
				TermFinder gtf = new TermFinder(cutoff, annotatedGenesInGenome, 
						"termFinder.pl", "gene_ontology_edit.obo", "gene_assoc_human.txt" );
				gtf.find(clusters);
				
				// Remove non-annotated clusters
				Iterator<Cluster> citer = clusters.iterator();
				while (citer.hasNext()) {
					Cluster c = citer.next();
					if (!c.isAnnotated()) {
						c.clear();
						citer.remove();
					}
				}
				
				// Legalize
				enrichLegalizer.manipulate(clusters);
				// Add neighboring prots
				neighborsAdder.manipulate(clusters);
				
				// Remove size < 3 clusters.
				clusterIter = clusters.iterator();
				while (clusterIter.hasNext()) {
					Cluster c = clusterIter.next();
					if (c.size() < MIN_CLUSTER_SIZE) {
						c.clear();
						clusterIter.remove();
					}
				}
				
				int genesBelongingToCluster = 0;
				for (Gene g: geneMap.values()) {
					if (g.isAnnotated() && g.clustersCount() != 0) {
						++genesBelongingToCluster;
					}
				}
				
				// Print output
				output += (netFileName + "\tMCL\t" + cutoff + "\tY\tY\t"  + alpha + "\t" + clusters.size() + "\t" + 
						genesBelongingToCluster + "\t" + (double)genesBelongingToCluster / annotatedGenes) + "\n";
				System.out.println(netFileName + "\tMCL\t" + cutoff + "\tY\tY\t" + alpha + "\t" + clusters.size() + "\t" + 
						genesBelongingToCluster + "\t" + (double)genesBelongingToCluster / annotatedGenes + "\n");				
				
				// Clear MCL stuff
				for (Cluster clus: clusters) {
					clus.clear();
				}
			} // end for cutoffs.
			

			
			for (double cutoff: cutoffs) { // bug fix...
			
			// Run NetworkBlast
			clusters = networkBLAST.getClusters(net, geneMap);
			 
			// NB - Solve overlaps
			OverlapsSolver olSover = new OverlapsSolverRWCAllowSingleOL(MIN_CLUSTER_SIZE , net);
			olSover.solve(clusters);
			
//			for (double cutoff: cutoffs) {
				TermFinder gtf = new TermFinder(cutoff, annotatedGenesInGenome, 
						"termFinder.pl", "gene_ontology_edit.obo", "gene_assoc_human.txt" );
				gtf.find(clusters);
				
				// Remove non-annotated clusters
				Iterator<Cluster> citer = clusters.iterator();
				while (citer.hasNext()) {
					Cluster c = citer.next();
					if (!c.isAnnotated()) {
						c.clear();
						citer.remove();
					}
				}
				
				// Legalize
				enrichLegalizer.manipulate(clusters);
				// Add neighboring prots
				neighborsAdder.manipulate(clusters);
				
				// Remove size < 3 clusters.
				Iterator<Cluster> clusterIter2 = clusters.iterator();
				while (clusterIter2.hasNext()) {
					Cluster c = clusterIter2.next();
					if (c.size() < MIN_CLUSTER_SIZE) {
						c.clear();
						clusterIter2.remove();
					}
				}
				
				int genesBelongingToCluster = 0;
				for (Gene g: geneMap.values()) {
					if (g.isAnnotated() && g.clustersCount() != 0) {
						++genesBelongingToCluster;
					}
				}
				
				// Print output
				output += netFileName + "\tNetworkBLAST\t" + cutoff + "\tY\tY\t" + alpha + "\t" + clusters.size() + "\t" + 
						genesBelongingToCluster + "\t" + (double)genesBelongingToCluster / annotatedGenes + "\n";
				System.out.println(netFileName + "\tNetworkBLAST\t" + cutoff + "\tY\tY\t" + alpha + "\t" + clusters.size() + "\t" + 
						genesBelongingToCluster + "\t" + (double)genesBelongingToCluster / annotatedGenes + "\n");

				// Clear NetworkBLAST stuff
				for (Cluster clus: clusters) {
					clus.clear();
				}
				
			} // for cutoffs (NetworkBLAST)
		} // while netFiles.
		
		try {
			FileWriter outputFile = new FileWriter(Consts.DATA_DIR+ "NewExperimentReweight" + Double.toString(alpha) + ".csv");
			outputFile.write(output);
			outputFile.close();
		} catch (IOException ioe) {
			System.out.println(ioe.getMessage());
			System.exit(1);
		}
			
			

			// Remove < 3 sized clusters.

			
		
		//	countOverlapsAndUnclustered(geneMap);
			
		/*	
			// Test removing gene from cluster
			Iterator<Cluster> citer = clusters.iterator();
			citer.next();citer.next();citer.next();citer.next();
			Cluster ctemp = citer.next();
			System.out.println("Before: " + ctemp.toString());
			System.out.println("Clusters ATXN1 participate in: " + geneMap.get("ATXN1").clustersCount());		
			ctemp.remove(geneMap.get("ATXN1"));
			System.out.println("After: " + ctemp.toString());
			System.out.println("Clusters ATXN1 participate in: " + geneMap.get("ATXN1").clustersCount());
	*/
			/*
			
			System.out.println("GO TERM FINDER");
			System.out.println("**********************");
			double cutoff = 0.05;
			TermFinder gtf = new TermFinder(cutoff, annotatedGenesInGenome, 
					"termFinder.pl", "gene_ontology_edit.obo", "gene_assoc_human.txt" );
			gtf.find(clusters);
			
			System.out.println("AFTER GO TERM FINDER AND FILTERING NON ANNOTATED CLUSTERS:");
			
			Iterator<Cluster> citer = clusters.iterator();
			while (citer.hasNext()) {
				Cluster c = citer.next();
				if (c.isAnnotated()) {
					System.out.println(c.getAnnotation() + "\t" +  c.toString());	
				}
				else {
					c.clear();
					citer.remove();
				}
			}
			
			ClusteringEvaluator evaluator 				= new ClusteringEvaluator(a, geneMap);
			ClusteringEvaluator.Results evalStats 	= new ClusteringEvaluator.Results();
			double clusteringGrade = evaluator.evaluate(clusters, evalStats);
			
			System.out.println("\tCLUSTERING EVALUATION, MCL");
			System.out.println("\t\t* No manipulators:");
			System.out.println("\t\tClusters: "+ clusters.size());
			countOverlapsAndUnclustered(geneMap);		
			System.out.println("\t\t\tFraction: " + clusteringGrade);
			System.out.println("\t\t\tAnnotated genes in correct clusters:\t" + evalStats.totalGenesInClustersWDominantAnnot);
			System.out.println("\t\t\tAnnotated genes total:\t" + evalStats.totalAnnotatedGenes);		

			enrichLegalizer.manipulate(clusters);
			
			clusteringGrade = evaluator.evaluate(clusters, evalStats);		
			System.out.println("\t\t* remove non-essential nodes:");
			System.out.println("\t\tClusters: "+ clusters.size());
			countOverlapsAndUnclustered(geneMap);
			System.out.println("\t\t\tFraction: " + clusteringGrade);
			System.out.println("\t\t\tAnnotated genes in correct clusters:\t" + evalStats.totalGenesInClustersWDominantAnnot);
			System.out.println("\t\t\tAnnotated genes total:\t" + evalStats.totalAnnotatedGenes);		

			neighborsAdder.manipulate(clusters);
			
			citer = clusters.iterator();
			while (citer.hasNext()) {
				Cluster c = citer.next();
				if (c.size() < 3) {
					c.clear();
					citer.remove();
				}			
			}		
			
			clusteringGrade = evaluator.evaluate(clusters, evalStats);		
			System.out.println("\t\t* add unclustered, correctly annotated neighbors (cluster size < 3):");
			System.out.println("\t\tClusters: "+ clusters.size());
			countOverlapsAndUnclustered(geneMap);
			System.out.println("\t\t\tFraction: " + clusteringGrade);
			System.out.println("\t\t\tAnnotated genes in correct clusters:\t" + evalStats.totalGenesInClustersWDominantAnnot);
			System.out.println("\t\t\tAnnotated genes total:\t" + evalStats.totalAnnotatedGenes);		
			
			// Clear MCL stuff
			for (Cluster clus: clusters) {
				clus.clear();
			}

			System.out.println("\nAfter clear:");		
			countOverlapsAndUnclustered(geneMap);
			
			System.out.println("\nNETWORK BLAST");
			System.out.println("*************************");		
			NetworkBlast blastNet = new NetworkBlast();
			clusters = blastNet.getClusters(net, geneMap);

			System.out.println("\tClusters: "+ clusters.size());
			countOverlapsAndUnclustered(geneMap);
			
			// Check how nasty is the overlap...
			System.out.println("How genes have k overlaps (0..64)");
			int overlaps[] = new int[65];
			for (Gene g: geneMap.values()) {
				overlaps[g.clustersCount()]++;
			}
			for (int i = 0; i < overlaps.length; i++) {
				System.out.print(overlaps[i] + "\t"); 
			}
			System.out.println();
			System.out.println("How overlaping genes each cluster has");
			overlaps = new int[21];
			int moreThanHalf = 0;
			int moreThanQuater = 0;
			for (Cluster c: clusters) {
				int overlappingGenes = 0;
				for (Gene g: c) {
					if (g.clustersCount() > 1) {
						overlappingGenes++;
					}
				}
				
				overlaps[overlappingGenes]++;
				
				if (overlappingGenes > (c.size() / 4)) {
					moreThanQuater++;
					if (overlappingGenes > (c.size() / 2)) {
						moreThanHalf++;
					}
				}
			}
			for (int i = 0; i < overlaps.length; i++) {
				System.out.print(overlaps[i] + "\t"); 
			}
			System.out.println("Clusters with > 25% overlapping genes: " + moreThanQuater);
			System.out.println("Clusters with > 50% overlapping genes: " + moreThanHalf);		
			System.out.println();
			
//			OverlapsSolver olSolver = new OverlapsSolverRemoveWeakerCluster();

			OverlapsSolver olSover = new OverlapsSolverRWCAllowSingleOL(3, net);
			olSover.solve(clusters);
			
			System.out.println("\tOverlap resolve: After using weaker cluster remover");
			System.out.println("\tClusters: "+ clusters.size());
			countOverlapsAndUnclustered(geneMap);
			
			System.out.println("GO TERM FINDER FOR NETWORK BLAST");
			gtf.find(clusters);

			citer = clusters.iterator();
			while (citer.hasNext()) {
				Cluster c = citer.next();
				if (c.isAnnotated()) {
					System.out.println(c.getAnnotation() + "\t" + c.toString());	
				}
				else {
					c.clear();
					citer.remove();
				}
			}
			

			clusteringGrade = evaluator.evaluate(clusters, evalStats);
			
			System.out.println("\tCLUSTERING EVALUATION, NetworkBLAST");
			System.out.println("\t\t* No manipulators:");
			System.out.println("\t\tClusters: "+ clusters.size());
			countOverlapsAndUnclustered(geneMap);		
			System.out.println("\t\t\tFraction: " + clusteringGrade);
			System.out.println("\t\t\tAnnotated genes in correct clusters:\t" + evalStats.totalGenesInClustersWDominantAnnot);
			System.out.println("\t\t\tAnnotated genes total:\t" + evalStats.totalAnnotatedGenes);		

			enrichLegalizer.manipulate(clusters);
			
			clusteringGrade = evaluator.evaluate(clusters, evalStats);		
			System.out.println("\t\t* remove non-essential nodes:");
			System.out.println("\t\tClusters: "+ clusters.size());
			countOverlapsAndUnclustered(geneMap);
			System.out.println("\t\t\tFraction: " + clusteringGrade);
			System.out.println("\t\t\tAnnotated genes in correct clusters:\t" + evalStats.totalGenesInClustersWDominantAnnot);
			System.out.println("\t\t\tAnnotated genes total:\t" + evalStats.totalAnnotatedGenes);

			neighborsAdder.manipulate(clusters);
			
			clusteringGrade = evaluator.evaluate(clusters, evalStats);		
			
			citer = clusters.iterator();
			while (citer.hasNext()) {
				Cluster c = citer.next();
				if (c.size() < 3) {
					c.clear();
					citer.remove();
				}			
			}
			
			System.out.println("\t\t* add unclustered, correctly annotated neighbors (clusters size > 3:");
			System.out.println("\t\tClusters: "+ clusters.size());
			countOverlapsAndUnclustered(geneMap);
			System.out.println("\t\t\tFraction: " + clusteringGrade);
			System.out.println("\t\t\tAnnotated genes in correct clusters:\t" + evalStats.totalGenesInClustersWDominantAnnot);
			System.out.println("\t\t\tAnnotated genes total:\t" + evalStats.totalAnnotatedGenes);		
			
		} */

	}
}
